iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 8
2
自我挑戰組

30天學python系列 第 8

[Day08] 物件導向編程基礎

  • 分享至 

  • xImage
  •  

把一組數據結構和處理它們的方法組成物件 (object),把相同行為的對象歸納為類別 (class),通過 class 的封裝 (encapsulation) 隱藏內部細節,透過繼承 (inheritance) 實現 class 的特化 (specialization) 和泛化 (generalization),通過多型 (polymorphism) 實現基於物件類別的動態分配。

類別 (class) 和物件 (object)

class 是 object 的藍圖和模板,而 object 是 class 的實例。class 是抽象的概念,而 object 是具體的東西。
在物件導向編程的中,一切皆為 object,object 都有屬性和行為。
每個 object 都是獨一無二的,而且 object 一定屬於某個 class。
當我們把一堆擁有共同特徵的 object 的靜態特徵 (屬性)和動態特徵(行為)都取出來後,就可以定義出一個 class。

定義 class,創建和使用 object

使用 class 關鍵字定義 class,然後在 class 中透過函數來定義方法 (Method),這樣就可以將 object 的動態特徵描述出來。

class Student(object):

    # __init__是一個特殊方法用於創建 object 時進行初始化
    # 我們可以為學生這個 object 綁定 name 和 age 兩個屬性
    def __init__ (self, name, age):
        self.name = name
        self.age = age

    def study (self, course_name):
        print('%s 正在學習 %s' % (self.name, course_name))

    def watch_movie(self):
        if self.age < 18:
            print('%s 只能觀看普通電影' % self.name)
        else:
            print('%s 能觀看所有電影' % self.name)

def main():
    
    stu1 = Student('Andy', 38)    # 創建學生並指定姓名和年齡
    stu1.study('Python 程式設計')  # 給 object 發 study 的消息
    stu1.watch_movie()            # 给 object 發 watch_movie 的消息
    stu2 = Student('Amy', 15)
    stu2.study('C 語言程式設計')
    stu2.watch_movie()

if __name__ == '__main__':
    main()

https://ithelp.ithome.com.tw/upload/images/20190923/20121116yWunRUdPC8.png

訪問權限

在物件導向編程中,通常會將 object 的屬性設為私有的 (private) 或受保護的 (protected),也就是不允許外界訪問,而 object 的 method 通常都是公開的 (public),因為公開的 method 就是 object 能夠接受的消息。
在 Python 中,屬性 和 method 的訪問權限只有兩種,公開和私有的,如果希望屬性是私有的,在給屬性命名時可以用兩個下劃線 ( __ ) 作為開頭。

class Test:

    def __init__ (self, foo):
        self.__foo = foo

    def __bar(self):
        print(self.__foo)
        print('__bar')

def main():
    test = Test('hello')
    # AttributeError: 'Test' object has no attribute '__bar'
    test.__bar()
    # AttributeError: 'Test' object has no attribute '__foo'
    print(test.__foo)

if __name__ == "__main__":
    main()

但是,Python並沒有從語法上嚴格保證私有屬性或方法的私密性,如果你知道更換名字的規則仍然可以訪問到它們。
在實際開發中,並不建議將屬性設置為私有的,因為這會導致子類別無法訪問。
所以大多 Python 會遵循一種命名慣例就是讓屬性名以單下劃線 ( _ ) 開頭來表示屬性是受保護的,這種做法並不是語法上的規則,單下劃線開頭的屬性和方法外界仍然是可以訪問的。

class Test:

    def __init__ (self, foo):
        self.__foo = foo

    def __bar(self):
        print(self.__foo)
        print('__bar')


def main():
    test = Test('hello')
    test._Test__bar()
    print(test._Test__foo)


if __name__ == "__main__":
    main()

https://ithelp.ithome.com.tw/upload/images/20190923/2012111636Tjtn2LS7.png

物件導向的支柱

物件導向有三大支柱:封裝、繼承和多型。
封裝:將 object 內部的資料隱藏起來,只向外界提供簡單的編程介面 (interface)。
在 class 中定義的 method 其實就是把數據和對數據的操作封裝起來,其他 object 即無法瞭解此 object 的內部細節。
在我們創建了 object 之後,只需要給 object 發送一個消息(調用 method)就可以執行當中的程式碼。
也就是說只需要知道 method 的名字和傳入的參數,而不需要知道方法內部的實現細節。
簡白的說,對一件事情只需要理解他的外在就好,不需要了解裡面內部的構造。

練習

練習1 - 定義一個 class 描述數字時鐘。

from time import sleep

class Clock(object):

    def __init__ (self, hour = 0, minute = 0, second = 0): # 初始化
        self._hour = hour           # 時
        self._minute = minute       # 分
        self._second = second       # 秒

    def run(self):
        self._second += 1            # 秒數 + 1
        if self._second == 60:       # 當秒數為 60, 分數 + 1, 秒數變回 0
            self._second = 0
            self._minute += 1
            if self._minute == 60:   # 當分數為 60, 時數 + 1, 分數變回 0
                self._minute = 0
                self._hour += 1
                if self._hour == 24: # 當時數為 24, 時數變為 0
                    self._hour = 0

    def show(self):        # 顯示時間
        return '%02d:%02d:%02d' % (self._hour, self._minute, self._second)

def main():
    clock = Clock(23, 59, 58)
    while True:
        print(clock.show())
        # sleep()函數推遲程式運行,單位為 secs 秒數
        sleep(1)        # 推遲進行 1 秒
        clock.run()

if __name__ == '__main__':
    main()

https://ithelp.ithome.com.tw/upload/images/20190924/20121116ZYy4tGTkai.png https://ithelp.ithome.com.tw/upload/images/20190924/20121116Wmvb2xEZKL.png
練習2 - 定義一個 class 描述平面上的點並提供移動點和計算到另一個點距離的方法。

from math import sqrt

class Point(object):

    def __init__ (self, x = 0, y = 0): # 初始化 
        self.x = x                # x 座標
        self.y = y                # y 座標

    def move_to (self, x, y):  # 移動到指定位置
        self.x = x            # x 座標
        self.y = y            # y 座標

    def move_by (self, dx, dy):  # 移動指定的增量
        self.x += dx            # x 座標的增量
        self.y += dy            # y 座標的增量

    def distance_to (self, other): # 計算兩點的距離 other:另一個點
        dx = self.x - other.x   
        dy = self.y - other.y
        return sqrt(dx ** 2 + dy ** 2)

    def __str__(self):
        return '(%s, %s)' % (str(self.x), str(self.y))

def main():
    p1 = Point(3, 5)
    p2 = Point()      # (0, 0)
    print(p1)
    print(p2)
    p1.move_to(-1, 2)
    p2.move_by(3, 5)
    print(p1)
    print(p2)
    print(p1.distance_to(p2))

if __name__ == '__main__':
    main()

https://ithelp.ithome.com.tw/upload/images/20190924/20121116LemNzmItTs.png


上一篇
[Day07] 字串和常用數據結構
下一篇
[Day09] 物件導向進階
系列文
30天學python30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言